
/**
 ***********************************************************************
 * @brief 图片编辑
 * 
 ************************************************************************
 */
#include <jni.h>
#include <jni_md.h>
#include <string>
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

#include "picture_edit.h"



/**
 * @brief 图片读取
 * 
 * 
 */
int read_buffer(void *opaque, uint8_t *buf, int buf_size){
	int size = -1;
	picture_edit_t *edit_ctx = (picture_edit_t *)opaque;
	if(edit_ctx->in_max <= edit_ctx->in_length)
	{
		return -1;
	}
	if((edit_ctx->in_max - edit_ctx->in_length) > buf_size)
	{
		size = buf_size;
	}
	else
	{
		size = edit_ctx->in_max - edit_ctx->in_length;
	}

	memcpy(buf, edit_ctx->in_data+edit_ctx->in_length, buf_size);
	edit_ctx->in_length += size;

	return size;
}
 
/**
 * @brief 图片写入
 * 
 * 
 */
int write_buffer(void *opaque, uint8_t *buf, int buf_size)
{
	picture_edit_t *edit_ctx = (picture_edit_t *)opaque;
	while((edit_ctx->out_length + buf_size) > edit_ctx->out_max)
	{
		edit_ctx->out_data = (uint8_t *)realloc(edit_ctx->out_data, edit_ctx->out_max << 1);
		if(NULL == edit_ctx->out_data)
			return -1;
		edit_ctx->out_max = edit_ctx->out_max << 1;
	}
	
	memcpy(edit_ctx->out_data+edit_ctx->out_length, buf, buf_size);
	edit_ctx->out_length += buf_size;

	return buf_size;
}
 
int write_file(uint8_t *data, uint32_t length)
{
	FILE *out_file;
	out_file = fopen("test_11.jpg","wb+");
	if(!out_file)
	{
		printf("open %s failed \n", "test_1.jpg");
		return -1;
	}

	fwrite(data, 1, length, out_file);
	fclose(out_file);
	return 0;
}


int picture_edit_init(picture_edit_t *edit_ctx, uint8_t *in_data, uint32_t len, const char *filter_descr)
{
    memset(edit_ctx, 0, sizeof(picture_edit_t));

	edit_ctx->in_data = in_data;
	edit_ctx->in_max = len;
	edit_ctx->in_length = 0;

	// edit_ctx->out_file = fopen("test_22.jpg","wb+");

	edit_ctx->out_data = (uint8_t *)malloc(DEFAULT_PICTURE_SIZE);
	if(NULL == edit_ctx->out_data)
	{
		printf("Error malloc %d failed \n", DEFAULT_PICTURE_SIZE);
		return -1;
	}
	edit_ctx->out_max  = DEFAULT_PICTURE_SIZE;
	edit_ctx->out_length = 0;

    if(0 != picture_codec_init(&edit_ctx->ffmpeg_context, edit_ctx, read_buffer, write_buffer, filter_descr))
	{
		printf("ffmpeg_init failed \n");
		return -1;
	}
	return 0;
}


void picture_edit_exit(picture_edit_t *edit_ctx)
{
    // fclose(edit_ctx->out_file);
	free(edit_ctx->out_data);
    picture_codec_exit(&edit_ctx->ffmpeg_context);
}


extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_myapp2_slice_MainAbilitySlice_stringFromJNI(JNIEnv* env, jobject  obj) {
	std::string hello = "Hello from JNI C++ codes";
	int len = hello.size();
	jchar res[len];
	for (int i = 0; i < len; i++) {
		res[i] = (jchar)hello[i];
	}
	return env->NewString(res, len);
}


/**
 * @brief 亮度
 * 	Luminance: 指的是投射在固定方向和面积上面的发光强度，发光强度是一个可测量的属性
 *  Brightness: 亮度是光的主观属性，显示器从暗到亮之间可以调节成不同程度等级的亮度，不能通过测量来客观评估（但可以说成比例，如50%的亮度）。
 * 
 * eq=brightness 效果和 hue=b=%f 一致
 */
extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_example_myapp2_slice_MainAbilitySlice_changePictureLuminance(JNIEnv* env, jobject  obj, jbyteArray source, jfloat luminace)
{
	char filter_descr[40] = {0}; 
	picture_edit_t edit_ctx;
	uint8_t *in_data = (uint8_t *)env->GetByteArrayElements(source, 0);
	uint32_t length = env->GetArrayLength(source);

    if(luminace > 10.0) luminace = 10.0;
	if(luminace < -10.0) luminace = -10.0;	
	// sprintf(filter_descr, "eq=brightness=%f", luminace);
	sprintf(filter_descr, "hue=b=%.1f", luminace);

    if(picture_edit_init(&edit_ctx, in_data, length, (const char *)filter_descr))
	{
		printf("picture_edit_init failed \n");
		return NULL;
	}

	picture_codec_process(&edit_ctx.ffmpeg_context);

	jbyteArray byteArray = env->NewByteArray(edit_ctx.out_length);
	jbyte *byte = env->GetByteArrayElements(byteArray, NULL);

	memcpy(byte,  edit_ctx.out_data, edit_ctx.out_length);

	//4.释放资源
	env->ReleaseByteArrayElements(byteArray, byte, 0);

	picture_edit_exit(&edit_ctx);

	return byteArray;
}


/**
 * @brief 对比度
 * @details
 * 		contrast 	是对比度
 * 		brightness 	是明亮 可以是小数
 * 
 */
extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_example_myapp2_slice_MainAbilitySlice_changePictureConstrast(JNIEnv* env, jobject  obj, jbyteArray source, jfloat constrast)
{
	char filter_descr[40] = {0};
	picture_edit_t edit_ctx;
	uint8_t *in_data = (uint8_t *)env->GetByteArrayElements(source, 0);
	uint32_t length = env->GetArrayLength(source);

	if(constrast > 9.0) constrast = 9.0;
	if(constrast < 1.0) constrast = 1.0;
	// 增加对比度
	if(constrast > 0)
		sprintf(filter_descr, "eq=contrast=%.1f", constrast);
	else // 模糊
		sprintf(filter_descr, "eq=contrast=%.1f", constrast);

	if(picture_edit_init(&edit_ctx, in_data, length, (const char *)filter_descr))
	{
		printf("picture_edit_init failed \n");
		return NULL;
	}

	picture_codec_process(&edit_ctx.ffmpeg_context);

	jbyteArray byteArray = env->NewByteArray(edit_ctx.out_length);
	jbyte *byte = env->GetByteArrayElements(byteArray, NULL);

	memcpy(byte,  edit_ctx.out_data, edit_ctx.out_length);

	//4.释放资源
	env->ReleaseByteArrayElements(byteArray, byte, 0);

	picture_edit_exit(&edit_ctx);

	return byteArray;
}


/**
 * @brief 饱和度
 */
extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_example_myapp2_slice_MainAbilitySlice_changePictureSaturation(JNIEnv* env, jobject  obj, jbyteArray source, jfloat saturation)
{
    char filter_descr[40] = {0};
	picture_edit_t edit_ctx;
	uint8_t *in_data = (uint8_t *)env->GetByteArrayElements(source, 0);
	uint32_t length = env->GetArrayLength(source);

	if(saturation > 3.0) saturation = 3.0;
	if(saturation < -0) saturation = -0;
	sprintf(filter_descr, "eq=saturation=%.1f", saturation);

	if(picture_edit_init(&edit_ctx, in_data, length, (const char *)filter_descr))
	{
		printf("picture_edit_init failed \n");
		return NULL;
	}

	picture_codec_process(&edit_ctx.ffmpeg_context);

	jbyteArray byteArray = env->NewByteArray(edit_ctx.out_length);
	jbyte *byte = env->GetByteArrayElements(byteArray, NULL);

	memcpy(byte,  edit_ctx.out_data, edit_ctx.out_length);

	//4.释放资源
	env->ReleaseByteArrayElements(byteArray, byte, 0);

	picture_edit_exit(&edit_ctx);

	return byteArray;
}


/**
 * @brief 色温
 * @details 条件 红蓝两色，负数调节红色，正数数添加蓝色
 * 			每种颜色的范围是 [-1.0 1.0]
 */
extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_example_myapp2_slice_MainAbilitySlice_changePictureTemperature(JNIEnv* env, jobject  obj, jbyteArray source, jfloat temperature)
{
	char filter_descr[40] = {0};
	picture_edit_t edit_ctx;
	uint8_t *in_data = (uint8_t *)env->GetByteArrayElements(source, 0);
	uint32_t length = env->GetArrayLength(source);

	if(temperature > 2.0) temperature = 2.0;
	if(temperature < -2.0) temperature = -2.0;
	if(temperature > 0)
	{
		sprintf(filter_descr, "colorbalance=rm=%.1f:bm=%.1f", 0.0, temperature-1.0);
	}
	else
	{
		sprintf(filter_descr, "colorbalance=rm=%.1f:bm=%.1f", temperature+1.0, 0.0);
	}

	if(picture_edit_init(&edit_ctx, in_data, length, (const char *)filter_descr))
	{
		printf("picture_edit_init failed \n");
		return NULL;
	}

	picture_codec_process(&edit_ctx.ffmpeg_context);

	jbyteArray byteArray = env->NewByteArray(edit_ctx.out_length);
	jbyte *byte = env->GetByteArrayElements(byteArray, NULL);

	memcpy(byte,  edit_ctx.out_data, edit_ctx.out_length);

	//4.释放资源
	env->ReleaseByteArrayElements(byteArray, byte, 0);

	picture_edit_exit(&edit_ctx);

	return byteArray;
}


/**
 * @brief  亮部: 在 Pshotoshop 中就是阴影和高光
 * @details 每个值的取值范围 [-1.0 1.0]
 */
extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_example_myapp2_slice_MainAbilitySlice_changePictureBrightPart(JNIEnv* env, jobject  obj, jbyteArray source, jfloat brightScale)
{
	char filter_descr[40] = {0};
	picture_edit_t edit_ctx;
	uint8_t *in_data = (uint8_t *)env->GetByteArrayElements(source, 0);
	uint32_t length = env->GetArrayLength(source);

	if(brightScale > 1.0) brightScale = 1.0;
	if(brightScale < -1.0) brightScale = -1.0;
	sprintf(filter_descr, "colorbalance=rh=%.1f:gh=%.1f:bh=%.1f", brightScale, brightScale, brightScale);

	if(picture_edit_init(&edit_ctx, in_data, length, (const char *)filter_descr))
	{
		printf("picture_edit_init failed \n");
		return NULL;
	}

	picture_codec_process(&edit_ctx.ffmpeg_context);

	jbyteArray byteArray = env->NewByteArray(edit_ctx.out_length);
	jbyte *byte = env->GetByteArrayElements(byteArray, NULL);

	memcpy(byte,  edit_ctx.out_data, edit_ctx.out_length);

	//4.释放资源
	env->ReleaseByteArrayElements(byteArray, byte, 0);

	picture_edit_exit(&edit_ctx);

	return byteArray;
}

/**
 * @brief 暗部
 */
extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_example_myapp2_slice_MainAbilitySlice_changePictureDarkPart(JNIEnv* env, jobject  obj, jbyteArray source, jfloat darkScale)
{
	char filter_descr[40] = {0};
	picture_edit_t edit_ctx;
	uint8_t *in_data = (uint8_t *)env->GetByteArrayElements(source, 0);
	uint32_t length = env->GetArrayLength(source);

	if(darkScale > 1.0) darkScale = 1.0;
	if(darkScale < -1.0) darkScale = -1.0;
	sprintf(filter_descr, "colorbalance=rs=%.1f:gs=%.1f:bs=%.1f", darkScale, darkScale, darkScale);

	if(picture_edit_init(&edit_ctx, in_data, length, (const char *)filter_descr))
	{
		printf("picture_edit_init failed \n");
		return NULL;
	}

	picture_codec_process(&edit_ctx.ffmpeg_context);

	jbyteArray byteArray = env->NewByteArray(edit_ctx.out_length);
	jbyte *byte = env->GetByteArrayElements(byteArray, NULL);

	memcpy(byte,  edit_ctx.out_data, edit_ctx.out_length);

	//4.释放资源
	env->ReleaseByteArrayElements(byteArray, byte, 0);

	picture_edit_exit(&edit_ctx);

	return byteArray;
}

/**
 * @brief 黑白
 * @details 彩色转换黑白
 *		y 参数调整亮度		    [0-256]
 *		u 参数调整蓝色平衡  	[0-256]
 *		v 参数调整红色平衡 		[0-256]
 */
extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_example_myapp2_slice_MainAbilitySlice_changeBlackAndWhite(JNIEnv* env, jobject  obj, jbyteArray source, jfloat scaleValue)
{
	char filter_descr[40] = {0};
	picture_edit_t edit_ctx;
	uint8_t *in_data = (uint8_t *)env->GetByteArrayElements(source, 0);
	uint32_t length = env->GetArrayLength(source);

	int value = (int)scaleValue;
	if(value > 256) value = 256;
	if(value < 0) value = 0;
	sprintf(filter_descr, "lutyuv=u=%d:v=%d", value, value);

	if(picture_edit_init(&edit_ctx, in_data, length, (const char *)filter_descr))
	{
		printf("picture_edit_init failed \n");
		return NULL;
	}

	picture_codec_process(&edit_ctx.ffmpeg_context);

	jbyteArray byteArray = env->NewByteArray(edit_ctx.out_length);
	jbyte *byte = env->GetByteArrayElements(byteArray, NULL);

	memcpy(byte,  edit_ctx.out_data, edit_ctx.out_length);

	//4.释放资源
	env->ReleaseByteArrayElements(byteArray, byte, 0);

	picture_edit_exit(&edit_ctx);

	return byteArray;
}


/**
 * @brief 锐度
 */
extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_example_myapp2_slice_MainAbilitySlice_changePictureSharpness(JNIEnv* env, jobject  obj, jbyteArray source, jfloat sharpness)
{
	char filter_descr[40] = {0};
	picture_edit_t edit_ctx;
	uint8_t *in_data = (uint8_t *)env->GetByteArrayElements(source, 0);
	uint32_t length = env->GetArrayLength(source);

	if(sharpness > 5.0) sharpness = 5.0;
	if(sharpness < -2.0) sharpness = -2.0;
	sprintf(filter_descr, "unsharp=5:5:%.1f", sharpness);

	if(picture_edit_init(&edit_ctx, in_data, length, (const char *)filter_descr))
	{
		printf("picture_edit_init failed \n");
		return NULL;
	}

	picture_codec_process(&edit_ctx.ffmpeg_context);

	jbyteArray byteArray = env->NewByteArray(edit_ctx.out_length);
	jbyte *byte = env->GetByteArrayElements(byteArray, NULL);

	memcpy(byte,  edit_ctx.out_data, edit_ctx.out_length);

	//4.释放资源
	env->ReleaseByteArrayElements(byteArray, byte, 0);

	picture_edit_exit(&edit_ctx);

	return byteArray;
}



